package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestVectorIntegration(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{
			input:    "(vector? #(1 2 3))",
			expected: "#t",
		},
		{
			input:    "(vector? '(1 2 3))",
			expected: "#f",
		},
		{
			input:    "(vector? '())",
			expected: "#f",
		},
		{
			input:    "(make-vector 3)",
			expected: "#(  )",
		},
		{
			input:    "(make-vector 2 5)",
			expected: "#(5 5)",
		},
		{
			input:    "(vector 1 2 3)",
			expected: "#(1 2 3)",
		},
		{
			input:    "(vector-length #(1 2 3))",
			expected: "3",
		},
		{
			input:    "(vector-ref #(10 20 30) 1)",
			expected: "20",
		},
		{
			input:    "(vector->list #(1 2 3))",
			expected: "(1 2 3)",
		},
		{
			input:    "(list->vector '(1 2 3))",
			expected: "#(1 2 3)",
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			lexer := lex.NewLexer(tt.input)
			tokens := lexer.ReadTokens()

			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			if result.Inspect() != tt.expected {
				t.Errorf("For input %q, expected %q, got %q", tt.input, tt.expected, result.Inspect())
			}
		})
	}
}

func TestVectorSetIntegration(t *testing.T) {
	// Test vector-set! by creating a vector, modifying it, and checking the result
	input := []string{
		"(define v (make-vector 3 0))",
		"(vector-set! v 1 5)",
		"v",
	}

	expected := []string{
		"",
		"",
		"#(0 5 0)",
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for i, inp := range input {
		t.Run(inp, func(t *testing.T) {
			lexer := lex.NewLexer(inp)
			tokens := lexer.ReadTokens()

			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			// For set! operations, we expect NULL_OBJECT which is displayed as empty string
			actual := result.Inspect()
			if result == obj.NULL_OBJECT {
				actual = ""
			}

			if actual != expected[i] {
				t.Errorf("For input %q, expected %q, got %q", inp, expected[i], actual)
			}
		})
	}
}

func TestVectorFillIntegration(t *testing.T) {
	// Test vector-fill! by creating a vector, filling it, and checking the result
	input := []string{
		"(define v (vector 1 2 3))",
		"(vector-fill! v 7)",
		"v",
	}

	expected := []string{
		"",
		"",
		"#(7 7 7)",
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for i, inp := range input {
		t.Run(inp, func(t *testing.T) {
			lexer := lex.NewLexer(inp)
			tokens := lexer.ReadTokens()

			parser := parse.NewParser(tokens)
			expr, err := parser.ParseExpression()
			if err != nil {
				t.Fatalf("ParseExpression() error = %v", err)
			}

			result, err := EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("EvalExpression() error = %v", err)
			}

			// For set! operations, we expect NULL_OBJECT which is displayed as empty string
			actual := result.Inspect()
			if result == obj.NULL_OBJECT {
				actual = ""
			}

			if actual != expected[i] {
				t.Errorf("For input %q, expected %q, got %q", inp, expected[i], actual)
			}
		})
	}
}
